package org.seckill.service.impl;

import java.util.Date;
import java.util.List;

import org.seckill.dao.SeckillDao;
import org.seckill.dao.SuccessKilledDao;
import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillEnum;
import org.seckill.dto.SeckillExecution;
import org.seckill.entity.Seckill;
import org.seckill.entity.SeckillSuccess;
import org.seckill.exception.RepeatKillException;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

@Service
public class SeckillServiceImpl implements SeckillService{

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private SeckillDao seckillDao;
	
	@Autowired
	private SuccessKilledDao successKilledDao;
	private final String salt = "werasdffawewiiii#$$Q@##@@,.";
	
	public List<Seckill> getSeckillList() {
		return seckillDao.queryAll(0, 10);
	}

	public Seckill getById(long seckillid) {
		return seckillDao.queryById(seckillid);
	}

	public Exposer exportSeckillUrl(long seckillid) {
		Seckill seckill = seckillDao.queryById(seckillid);
		if(seckill == null) {
			return new Exposer(false, seckillid);
		}
		Date startTime = seckill.getStartTime();
		Date endTime = seckill.getEndTime();
		Date curDate = new Date();
		//时间过了或者之前
		if(curDate.getTime() < startTime.getTime() || curDate.getTime() > endTime.getTime()) {
			return new Exposer(false, seckillid, curDate.getTime(), startTime.getTime(), endTime.getTime());
		}
		//返回正确结果
		String md5 = getMyMD5(seckillid);
		return new Exposer(true, md5, seckillid);
	}

	private String getMyMD5(long seckillid) {
		String base = seckillid + "/" + salt;
		return DigestUtils.md5DigestAsHex(base.getBytes());
	}

	@Transactional
	/**
	 * 使用注解控制事务：
	 * 1.明确事务的编程风格
	 * 2.保证事务方法执行时间尽可能短，不要穿插网络操作
	 * 3.事务应用于读写都有，只读任务不需要使用事务，只有一条修改，也不是必须
	 * 行级锁，mysql
	 */
	public SeckillExecution executeSeckill(long seckillid, long userPhone, String md5) 
			throws SeckillException, RepeatKillException, SeckillCloseException{
		//md5是否合法
		if(md5 == getMyMD5(seckillid)) {
			return new SeckillExecution(seckillid, SeckillEnum.DATA_REWRITE);
		}
		//合法执行减一
		try {
			Date nowTime = new Date();
			//减库存操作
			int updateCount = seckillDao.reduceNumber(seckillid, nowTime); 
			
			if(updateCount < 0) {
				throw new SeckillCloseException("秒杀失败");
			}else {
				int inserCount = successKilledDao.insertSuccessKilled(seckillid, userPhone);
				if(inserCount <= 0) {
					throw new RepeatKillException("重复秒杀");
				}else {
					//秒杀成功,huoqu秒杀订单
					SeckillSuccess successKilled = successKilledDao.queryByIdWithSeckill(seckillid, userPhone);
					return new SeckillExecution(seckillid, SeckillEnum.SUCCESS, successKilled);
				}
			}
		}catch(SeckillCloseException e) {
			throw e;
//			logger.debug(e.toString());
		}catch(RepeatKillException e) {
			throw e;
//			logger.debug(e.toString());
		}catch(SeckillException e) {
			//将编译期异常转化为运行期异常,这样spring发现后，会回滚事务
			logger.debug(e.toString());
			throw new SeckillException("seckill inner error：" + e.getMessage());
		}
	}

}
